﻿//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Query Application Block
//===============================================================================

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;

namespace EntLibContrib.Query.Configuration.Unity
{
	/// <summary>
	/// A <see cref="UnityContainerExtension"/> that registers the policies necessary
	/// to create <see cref="ICustomCommand"/>, <see cref="IParameterSet"/> and <see cref="IQuery"/> instances
	/// described in the standard configuration file.
	/// </summary>
	public class QueryBlockExtension : EnterpriseLibraryBlockExtension
	{
		#region Protected Methods
		/// <summary>
		/// Initialize this extension by adding the Enterprise Library Query Block's policies.
		/// </summary>
		protected override void Initialize()
		{
			QuerySettings settings = (QuerySettings)ConfigurationSource.GetSection(QuerySettings.SectionName);

			if (settings == null)
				return;

			CreateProvidersPolicies<ICustomCommand, CustomCommandData>(Context.Policies,
																																null,
																																settings.Commands,
																																ConfigurationSource);

			CreateParameterSetsPolicies(Context.Policies, settings.ParameterSets);

			CreateProvidersPolicies<IQuery, QueryDataBase>(Context.Policies,
																										null,
																										settings.Queries,
																										ConfigurationSource);

			CreateCommandLifetimePolicies(Context.Container, settings.Commands);

			CreateParameterSetLifetimePolicies(Context.Container, settings.ParameterSets);

			CreateQueryLifetimePolicies(Context.Container, settings.Queries);
		}
		#endregion

		#region Private Static Methods
		private void CreateParameterSetsPolicies(IPolicyList policyList, IEnumerable<ParameterSetData> parameterSets)
		{
			foreach (var parameterSetData in parameterSets)
			{
				// this is required because Parameter names are only unique within a parameter set
				string parentPrefix = Guid.NewGuid().ToString();

				// this is required to create a type mapping between IParameterSet and ParameterSet concrete types
				policyList.Set<IBuildKeyMappingPolicy>(new BuildKeyMappingPolicy(new NamedTypeBuildKey(parameterSetData.Type, parameterSetData.Name)),
																							new NamedTypeBuildKey(typeof(IParameterSet), parameterSetData.Name));

				// this is the ParameterSet concrete type policy builder
				new PolicyBuilder<ParameterSet, ParameterSetData>(NamedTypeBuildKey.Make<ParameterSet>(parameterSetData.Name),
																													parameterSetData,
																													c => new ParameterSet(Resolve.ReferenceDictionary<ParameterDictionary, IParameter, string>(from p in c.Parameters select new KeyValuePair<string, string>(parentPrefix + p.Name, p.Name)))
																													).AddPoliciesToPolicyList(policyList);

				// this is the Parameter policy builder resolving unique key names for parameters matched with the parameter set
				CreateProvidersPolicies<IParameter, ParameterData>(policyList,
																													null,
																													parameterSetData.Parameters,
																													ConfigurationSource,
																													h => parentPrefix + h.Name);
			}
		}

		private static void CreateCommandLifetimePolicies(IUnityContainer container, IEnumerable<CustomCommandData> commands)
		{
			foreach (var commandData in commands)
			{
				container.RegisterType(commandData.Type, commandData.Name, new ContainerControlledLifetimeManager());
			}
		}

		private static void CreateParameterSetLifetimePolicies(IUnityContainer container, IEnumerable<ParameterSetData> parameterSets)
		{
			foreach (var parameterSetData in parameterSets)
			{
				container.RegisterType(parameterSetData.Type, parameterSetData.Name, new ContainerControlledLifetimeManager());
			}
		}

		private static void CreateQueryLifetimePolicies(IUnityContainer container, IEnumerable<QueryDataBase> queries)
		{
			foreach (var queryData in queries)
			{
				container.RegisterType(queryData.Type, queryData.Name, new ContainerControlledLifetimeManager());
			}
		}
		#endregion
	}
}
